package ulht.ia;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import java.util.Stack;
import Interface.*;

public class Horario {

    /**
     * Salas, disciplinas e turmas utilizadas na geração de horários
     */
    private Salas salas = new Salas();
    private Disciplinas disciplinas = new Disciplinas();
    private Turmas turmas = new Turmas();
    /**
     * Array que contém o horário de cálculo (5 tabelas com 8 linhas e 5 colunas
     */
    private HashMap<String, String[][]> horarioSalas = new HashMap<>();
    /**
     * Array que contém o horário na perspectiva das salas
     */
    private ArrayList<String[][]> horario = new ArrayList<>();
    /**
     * Array que contém o horário na perspectiva das turmas
     */
    private HashMap<String, String[][]> horarioTurmas = new HashMap<>();
    /**
     * Array que contém o horário na perspectiva das disciplinas (professores)
     */
    private HashMap<String, String[][]> horarioDisciplinas = new HashMap<>();
    /**
     * Array vazio para a inicialização de salas
     */
    private String horarioVazio[][] = {{"", "", "", "", ""}, //08-10
        {"", "", "", "", ""}, //10-12
        {"", "", "", "", ""}, //12-14
        {"", "", "", "", ""}, //14-16
        {"", "", "", "", ""}, //16-18
        {"", "", "", "", ""}, //18-20
        {"", "", "", "", ""}, //20-22
        {"", "", "", "", ""}}; //22-24
    /**
     * Array que vai conter o horário Final. 8 linhas (slots de tempo) com 5
     * colunas (2ª, 3ª, 4ª, 5ª, 6ª). O conteúdo da slot é uma string com sala,
     * disciplina, turma ex: S100305
     */
    private String horarioTotal[][] = {{"", "", "", "", ""}, //08-10
        {"", "", "", "", ""}, //10-12
        {"", "", "", "", ""}, //12-14
        {"", "", "", "", ""}, //14-16
        {"", "", "", "", ""}, //16-18
        {"", "", "", "", ""}, //18-20
        {"", "", "", "", ""}, //20-22
        {"", "", "", "", ""}}; //22-24
    /**
     * Stack que terá os tuplos disciplinas/turmas (duplicadas para duas
     * sessões), que serao utilizadas na geração inicial de horários
     */
    private Stack stackDisciplinas = new Stack();

    /*
     * -------------------------------------------------------------------------
     * Métodos
     -------------------------------------------------------------------------
     */
    /**
     * Cria o stack com as strings de Disciplina com turma
     */
    private void buildStack() {
        for (String disc : this.disciplinas.getListaDisciplinas().keySet()) {
            if (!disc.equals("000")) {
                for (String trm : this.turmas.getListaTurmas().keySet()) {
                    if (!trm.equals("00")) {
                        stackDisciplinas.add(disc + trm);
                        stackDisciplinas.add(disc + trm);
                    }
                }
            }
        }
//        //debug
//        while(!stackDisciplinas.isEmpty())
//            System.out.println(stackDisciplinas.pop());
    }

    private boolean checkTurmaDisponivel(int dia, int slot, String turma) {
        for (int s = 1; s < this.salas.getListaSalas().size(); s++) {
            if (horario.get(s)[slot][dia].endsWith(turma)) {
                return false;
            }
        }
        return true;
    }

    private void geraHorario() {
        Random aleatorio = new Random();

        int sala = aleatorio.nextInt(salas.getListaSalas().size() - 1) + 1;
        int dia = aleatorio.nextInt(5);
        int slot = aleatorio.nextInt(8);

        if (this.stackDisciplinas.size() <= 0) {
            this.buildStack();
        }

        for (int i = 0; i < salas.getListaSalas().size(); i++) {
            String horarioLocal[][] = new String[8][5];
            for (int x = 0; x < 8; x++) {
                for (int y = 0; y < 5; y++) {
                    horarioLocal[x][y] = "";
                }
            }
            this.horario.add(horarioLocal);
        }

        while (!this.stackDisciplinas.isEmpty()) {
            while ((!horario.get(sala)[slot][dia].isEmpty()) || (!checkTurmaDisponivel(dia, slot, this.stackDisciplinas.peek().toString().substring(this.stackDisciplinas.firstElement().toString().length() - 2, this.stackDisciplinas.firstElement().toString().length())))) {
                sala = aleatorio.nextInt(salas.getListaSalas().size() - 1) + 1;
                dia = aleatorio.nextInt(5);
                slot = aleatorio.nextInt(8);
            }
            this.horario.get(sala)[slot][dia] = stackDisciplinas.pop().toString();
        }

        // gera o horário na perspectiva das turmas (Hashmap com código da sala e array de horário)
        setHorarioSalas();

        // gera o horário para exibição com todas as salas
        setHorarioTotal();

        // gera o horário na perspectiva das turmas (Hashmap com código da turma e array de horário)
        setHorarioTurmas();

        // gera o horário na perspectiva do professor (disciplinas) (Hashmap com código da disciplina e array de horário)
        setHorarioDisciplinas();
    }

    /**
     * Gera o horário total
     */
    private void setHorarioTotal() {
        String linha = "";
        for (int slot = 0; slot <= 7; slot++) {
            for (int dia = 0; dia <= 4; dia++) {
                for (int sala = 1; sala < this.horario.size(); sala++) {
                    linha += this.salas.getListaSalas().get("0" + sala);
                    linha += this.horario.get(sala)[slot][dia].isEmpty() ? "00000" : this.horario.get(sala)[slot][dia];
                }
                this.horarioTotal[slot][dia] = linha;
                linha = "";
            }
        }
    }

    /**
     * Devolve o array com o horário completo
     *
     * @return String horario[][]
     */
    public String[][] getHorarioTotal() {
        return this.horarioTotal;
    }

    /**
     * Cria o horário na perspectiva das Salas (propriedade do Objecto)
     */
    private void setHorarioSalas() {
        for (int i = 0; i < salas.getListaSalas().size(); i++) {
            String horarioLocal[][] = new String[8][5];
            for (int x = 0; x < 8; x++) {
                for (int y = 0; y < 5; y++) {
                    horarioLocal[x][y] = "";
                }
            }
        }
        for (int sala = 1; sala < salas.getListaSalas().size(); sala++) {
            String horarioLocal[][] = new String[8][5];
            for (int x = 0; x < 8; x++) {
                for (int y = 0; y < 5; y++) {
                    horarioLocal[x][y] = "";
                }
            }
            for (int slot = 0; slot < 8; slot++) {
                for (int dia = 0; dia < 5; dia++) {
                    if (!this.horario.get(sala)[slot][dia].isEmpty()) {
                        horarioLocal[slot][dia] = this.horario.get(sala)[slot][dia].substring(0, this.horario.get(sala)[slot][dia].length() - 2) + " " + this.horario.get(sala)[slot][dia].substring(this.horario.get(sala)[slot][dia].length() - 2, this.horario.get(sala)[slot][dia].length()) + "  ";
                    }
                }
            }
            this.horarioSalas.put("0" + sala, horarioLocal);
        }
   }

    /**
     * Cria o horário na perspectiva das Turmas (propriedade do Objecto)
     */
    private void setHorarioTurmas() {
        for (int i = 0; i < turmas.getListaTurmas().size(); i++) {
            String horarioLocal[][] = new String[8][5];
            for (int x = 0; x < 8; x++) {
                for (int y = 0; y < 5; y++) {
                    horarioLocal[x][y] = "";
                }
            }
        }
        for (String turma : this.turmas.getListaTurmas().keySet()) {
            String horarioLocal[][] = new String[8][5];
            for (int x = 0; x < 8; x++) {
                for (int y = 0; y < 5; y++) {
                    horarioLocal[x][y] = "";
                }
            }
            for (int sala = 1; sala < salas.getListaSalas().size(); sala++) {
                for (int slot = 0; slot < 8; slot++) {
                    for (int dia = 0; dia < 5; dia++) {
                        if (!this.horario.get(sala)[slot][dia].isEmpty()) {
                            if (this.horario.get(sala)[slot][dia].substring(this.horario.get(sala)[slot][dia].length() - 2, this.horario.get(sala)[slot][dia].length()).equalsIgnoreCase(turma)) {
                                horarioLocal[slot][dia] = this.horario.get(sala)[slot][dia].substring(0, this.horario.get(sala)[slot][dia].length() - 2) + " " + this.salas.getListaSalas().get("0" + sala) + "  ";
                            }
                        }
                    }
                }
            }
            this.horarioTurmas.put(turma, horarioLocal);
        }
    }

    /**
     * Devolve o horário na perspectiva das turmas
     *
     * @return HashMap<String, String[][]>
     */
    public HashMap<String, String[][]> getHorarioTurmas() {
        return horarioTurmas;
    }

    /**
     * Cria o horário na perspectiva das Disciplinas (propriedade do Objecto)
     */
    private void setHorarioDisciplinas() {
        for (int i = 0; i < disciplinas.getListaDisciplinas().size(); i++) {
            String horarioLocal[][] = new String[8][5];
            for (int x = 0; x < 8; x++) {
                for (int y = 0; y < 5; y++) {
                    horarioLocal[x][y] = "";
                }
            }
        }
        for (String disciplina : this.disciplinas.getListaDisciplinas().keySet()) {
            String horarioLocal[][] = new String[8][5];
            for (int x = 0; x < 8; x++) {
                for (int y = 0; y < 5; y++) {
                    horarioLocal[x][y] = "";
                }
            }
            for (int sala = 1; sala < salas.getListaSalas().size(); sala++) {
                for (int slot = 0; slot < 8; slot++) {
                    for (int dia = 0; dia < 5; dia++) {
                        if (!this.horario.get(sala)[slot][dia].isEmpty()) {
                            if (this.horario.get(sala)[slot][dia].substring(0, this.horario.get(sala)[slot][dia].length() - 2).equalsIgnoreCase(disciplina)) {
                                horarioLocal[slot][dia] = this.horario.get(sala)[slot][dia].substring(this.horario.get(sala)[slot][dia].length() - 2, this.horario.get(sala)[slot][dia].length()) + " " + this.salas.getListaSalas().get("0" + sala) + "  ";
                            }
                        }
                    }
                }
            }
            this.horarioDisciplinas.put(disciplina, horarioLocal);
        }
    }

    /**
     * Devolve o horário na perspectiva das disciplinas (professores)
     *
     * @return HashMap<String, String[][]>
     */
    public HashMap<String, String[][]> getHorarioDisciplinas() {
        return this.horarioDisciplinas;
    }

    /**
     * Devolve o horário na perspectiva das salas
     *
     * @return HashMap<String, String[][]>
     */
    public HashMap<String, String[][]> getHorarioSalas() {
        return this.horarioSalas;
    }

    /**
     * Constructor com valores default
     */
    public Horario() {
        this.geraHorario();
    }

    /**
     * Constructor com parâmetros externos
     *
     * @param salas
     * @param disciplinas
     * @param turmas
     */
    public Horario(Salas salas, Disciplinas disciplinas, Turmas turmas) {
        this.salas = salas;
        this.disciplinas = disciplinas;
        this.turmas = turmas;
        this.geraHorario();
    }
}
